/*
 * Copyright (c) 2009 Project E7
 *
 * This file is part of ZebBowling
 *
 * ZebBowling is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * ZebBowling is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with ZebBowling. If not, see <http://www.gnu.org/licenses/>.
 **/

package org.zebbowling.world;

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import javax.imageio.ImageIO;
import javax.swing.Timer;

/**
 *
 * @author Mark
 */
public class BowlingLane extends Canvas implements KeyListener, ActionListener {

    public static final Color ALLEY_COLOR = new Color(180, 116, 68);

    protected ArrayList bowlingPins;
    protected BowlingPlayer[] players;
    protected BowlingPlayer currPlayer;
    protected boolean[] pressed;
    //0,1,2,3 arrow keys, 4 space bar, 5 enter key
    protected CanvasObject ball;
    protected Timer runner;
    protected BufferedImage back;
    //powerMode waits for the user to do power, curve waits for the curve
    protected int power, curve, player, turn, rolls, currentState, lastState,
            lastScore;
    protected boolean isIncreasing;//Boolean used for power and curve bars
    protected String inputString;
    protected final String[] GAME_MODE = {"2 Player Mode", "3 Player Mode",
        "4 Player Mode"};
    protected int mode;//must be an index of GAME_MODE
    private boolean flash;
    protected BowlingFrame frame;

    /**
     * Creates a new BowlingLane
     * @param players - amount of players to play
     */
    public BowlingLane(BowlingFrame frame)
        {
        super();
        setBackground(ALLEY_COLOR);
        this.frame = frame;
        pressed = new boolean[6];
        mode = 0;
        runner = new Timer(BowlingFrame.MENU_INTERVAL, this);
        flash = false;
        addKeyListener(this);
        addPins();
        ball = new BowlingBall();
        ball.setPosition(120,650);
        ball.setVel(10);
        runner.start();//Starts Game loop
        //Initializes state machine
        currentState = lastState = BowlingGameState.MAIN_MENU_MODE;
        isIncreasing = true;
        power = 1;
        curve = BowlingFrame.GAME_WIDTH/2;
        inputString = "    ";
        player = turn = rolls = lastScore = 0;
        }

    /**
     * Resets the array so that the pins are all up
     */
    protected void addPins()
        {
        bowlingPins = new ArrayList();
        for (int i = 0; i < 4; i++)
            bowlingPins.add(new BowlingPin(45 + i * 50,0));
        for (int i = 0; i < 3; i++)
            bowlingPins.add(new BowlingPin(i * 50 + 70, 50));
        for (int i = 1; i < 3; i++)
            bowlingPins.add(new BowlingPin(45 + i * 50,100));
        bowlingPins.add(new BowlingPin(120, 150));
        }

    /**
     * Adds last key pressed to input String which is 4 characters long
     * Updates itself everytime a key is pressed
     * @param e
     */
    public void keyTyped(KeyEvent e)
        {
        if (!e.isActionKey())
            {
            inputString = inputString.substring(1, 4) + Character.toLowerCase(e.getKeyChar());
            if (inputString.equals("free"))
                {
                lastState = currentState;
                currentState = BowlingGameState.FREE_LANCE_MODE;
                }
            if (inputString.equals("done"))
                {
                currentState = lastState;
                lastState = BowlingGameState.FREE_LANCE_MODE;
                }
            }
        }


    public void keyPressed(KeyEvent e)
        {
        switch (e.getKeyCode())
            {
            case 37:
                pressed[0] = true;
                break;
            case 38:
                pressed[1] = true;
                break;
            case 39:
                pressed[2] = true;
                break;
            case 40:
                pressed[3] = true;
                break;
            case 32:
                pressed[4] = true;
                break;
            case 10:
                pressed[5] = true;
                if(currentState == BowlingGameState.MAIN_MENU_MODE)
                    startGame();
                else
                    pauseGame();
                break;
            }
        mode = pressed[1]? (mode - 1 < 0? mode: mode - 1):mode;
        mode = pressed[3]? (mode + 1 > 2? mode: mode + 1):mode;
        }

    public void menuUp()
        {
        mode = mode - 1 < 0? mode: mode - 1;
        }

    public void menuDown()
        {
        mode = mode + 1 > 2? mode: mode + 1;
        }

    public void keyReleased(KeyEvent e)
        {
       switch (e.getKeyCode())
            {
            case 37:
                pressed[0] = false;
                break;
            case 38:
                pressed[1] = false;
                break;
            case 39:
                pressed[2] = false;
                break;
            case 40:
                pressed[3] = false;
                break;
            case 32:
                pressed[4] = false;
                break;
            case 10:
                pressed[5] = false;
                break;
            }
        }

    public synchronized void startGame()
        {
        currentState = BowlingGameState.POWER_MODE;
        players = new BowlingPlayer[mode + 2];
        for (int i = 0; i < players.length; i++)
            players[i] = new BowlingPlayer(i + 1);
        frame.setSize(BowlingFrame.GAME_WIDTH, BowlingFrame.GAME_HEIGHT);
        runner.setDelay(BowlingFrame.GAME_INTERVAL);
        }

    public synchronized void pauseGame()
        {
        if (currentState == BowlingGameState.PAUSE_MODE)
            {
            if (turn > 9)
                return;
            currentState = lastState;
            lastState = BowlingGameState.PAUSE_MODE;
            runner.setDelay(BowlingFrame.GAME_INTERVAL);
            }
        else
            {
            lastState = currentState;
            currentState = BowlingGameState.PAUSE_MODE;
            }
        }

    /**
     * Refreshes the Frame
     * @param e
     */
    public void actionPerformed(ActionEvent e)
        {
        repaint();
        }

    /**
     * @Override
     *
     */    
    public void paint(Graphics g)
        {        
        //take a snap shop of the current screen and save it as an image
        //that is the exact same width and height as the current screen
        back = (BufferedImage) (createImage(getWidth(), getHeight()));

        //create a graphics reference to the back ground image
        //we will draw all changes on the background image
        Graphics graphToBack = back.createGraphics();
        boolean gameState = currentState != BowlingGameState.MAIN_MENU_MODE &&
                currentState != BowlingGameState.PAUSE_MODE;
        if (gameState)
            updateGameFrame(graphToBack);
        switch (currentState)
            {
            case BowlingGameState.MAIN_MENU_MODE:
                mainMenuState(graphToBack);
                if (currentState == BowlingGameState.MAIN_MENU_MODE)
                    break;
                else
                    return;
            case BowlingGameState.PAUSE_MODE:
                pauseState(graphToBack);
                break;
            case BowlingGameState.FREE_LANCE_MODE:
                freeLanceState();
                break;
            case BowlingGameState.POWER_MODE:
                powerState(graphToBack);
                break;
            case BowlingGameState.CURVE_MODE:
                curveState(graphToBack);
                break;
            case BowlingGameState.BOWL_MODE:
                bowlState(graphToBack);
                break;
            }
        if (gameState) {
            graphToBack.drawString("Player " + (player + 1) + " Turn " +
                    (turn + 1), 70, 720);
            int score = player == 0? players[players.length - 1].getScore(turn - 1):
                players[player - 1].getScore(turn);
            graphToBack.drawString("Previous Player Frame Score: "+ score ,40,730);
            ball.draw(graphToBack);
            }

        //draws buffered image to screen
        g.drawImage(back, 0, 0, null);
        }


    private void updateGameFrame(Graphics graphToBack)
    {
        frame.setSize(BowlingFrame.GAME_WIDTH, BowlingFrame.GAME_HEIGHT);
        setSize(BowlingFrame.GAME_WIDTH, BowlingFrame.GAME_HEIGHT);
        //If there has been 10 frames for each player
        if (turn > 9)
            {
            currentState = BowlingGameState.PAUSE_MODE;
            return;
            }
        if (pressed[5] && currentState != BowlingGameState.PAUSE_MODE)
            {
            lastState = currentState;
            currentState = BowlingGameState.PAUSE_MODE;
            }
        //goes through the array and sees if there are pins that collide with ball
        ArrayList temp = new ArrayList();
        for (int i = bowlingPins.size() - 1; i >= 0; i--)
            if (ball.collisionWith((CanvasObject)bowlingPins.get(i)))
                temp.add(bowlingPins.remove(i));
        for (int i = 0; i < temp.size(); i++)
            {
            CanvasObject fr = (CanvasObject)temp.get(i);
            int xDis = ball.getXPos() - fr.getXPos(), yDis = ball.getYPos()
                    - fr.getYPos();
            fr.setPosition(fr.getXPos() - (currPlayer.getCurve()/33 + 2) * xDis, fr.getYPos() -
                    (currPlayer.getPower()/75 + 2) * yDis);
            }
        for (int i = temp.size() - 1; i >= 0; i--)
            for (int x = 0; x < bowlingPins.size(); x++)
                if (((CanvasObject)temp.get(i)).collisionWith(((CanvasObject)
                    bowlingPins.get(x))))
                    bowlingPins.remove(x);
        //draws all the pins that weren't hit
        for (int i = 0; i < bowlingPins.size(); i++)
            ((CanvasObject)bowlingPins.get(i)).draw(graphToBack);        
    }

    protected void mainMenuState(Graphics graphToBack)
        {
       //Flashes the option, out of 3 options so far
        try {
        graphToBack.drawImage(ImageIO.read(new File("BowlingBanner.gif")), 0, 30, this);
        int height = 200;
        for (int i = 0; i < GAME_MODE.length; i++, height += 40)
            {
            Color temp = graphToBack.getColor();
            if (i == mode)
                {
                if (flash)
                    graphToBack.setColor(BowlingLane.ALLEY_COLOR);
                flash = !flash;
                }
            graphToBack.drawString(GAME_MODE[i], BowlingFrame.MENU_WIDTH/3, height);
            graphToBack.setColor(temp);
            }
        }
        catch (IOException ex)
            {
            ex.printStackTrace();
            }
        }

    protected void pauseState(Graphics graphToBack)
        {
        frame.setSize(BowlingFrame.GAME_HEIGHT, BowlingFrame.GAME_WIDTH);
        setSize(BowlingFrame.GAME_HEIGHT, BowlingFrame.GAME_WIDTH);
        runner.setDelay(BowlingFrame.MENU_INTERVAL);
        String status = turn > 9? "Game Over!": "Game Pause! Spot Left to Continue";
        try{
        graphToBack.drawImage(ImageIO.read(new File("BowlingBanner.gif")), 
                BowlingFrame.GAME_HEIGHT/2 - 180, 10, this);}
        catch (IOException io) {io.printStackTrace();}
        graphToBack.drawString(status, BowlingFrame.GAME_HEIGHT/2 - 80, 100);

        for (int i = 0; i < players.length; i++)
            {
            Color temp = graphToBack.getColor();
            if (i == player)
                {
                if (flash)
                    graphToBack.setColor(BowlingLane.ALLEY_COLOR);
                flash = !flash;
                }
            graphToBack.drawString("Player " + (i + 1) + ":", 30, 130 + (i * 30));
            for (int x = 0; x < 10; x++)
                graphToBack.drawString("" + players[i].getScore(x), 100 + (x * 60),
                (i * 30) + 130);
            graphToBack.drawString("Total: " + players[i].totalScore(), 700, (i * 30)
                    + 130);
            graphToBack.setColor(temp);
            }
        }

    /**
     * Calls this method in main game loop if state == free lance mode
     */
    protected void freeLanceState()
        {
        if (pressed[0])
            ball.setXPos((ball.getXPos() - 10) < 0?
                ball.getXPos(): ball.getXPos() - 10);
        if (pressed[1])
            ball.setYPos((ball.getYPos() - 10) < 0?
                ball.getYPos(): ball.getYPos() - 10);
        if (pressed[2])
            ball.setXPos((ball.getXPos() + 10) >
                    BowlingFrame.GAME_WIDTH? ball.getXPos():
                    ball.getXPos() + 10);
        if (pressed[3])
            ball.setYPos((ball.getYPos() + 10) >
                    BowlingFrame.GAME_HEIGHT?ball.getYPos():
                    ball.getYPos() + 10);
        }

    /**
     * Calls this method in main game loop if state == power mode
     * @param graphToBack
     */
    protected void powerState(Graphics graphToBack)
        {
        if (isIncreasing)
            power += 10;
        else
            power -= 10;
        Color temp = graphToBack.getColor();
        graphToBack.setColor(Color.RED);
        graphToBack.fillRect(0, 760, (int)(300 * (power/100.)), 40);
        graphToBack.setColor(temp);
        graphToBack.drawLine(0, 760, 300, 760);
        graphToBack.drawString("Press Space for Power", 70, 750);
        if (power >= 100)
            isIncreasing = false;
        if (power < 0)
            isIncreasing = true;
        if (pressed[4])
            {
            pressed[4] = false;
            lastState = currentState;
            currentState = BowlingGameState.CURVE_MODE;
            }
        }

    /**
     * Calls this method in main game loop if state == curve mode
     * @param graphToBack
     */
    protected void curveState(Graphics graphToBack)
        {
        if (isIncreasing)
            curve += 10;
        else
            curve -= 10;
        Color temp = graphToBack.getColor();
        graphToBack.setColor(Color.RED);
        graphToBack.drawLine(BowlingFrame.GAME_WIDTH/2, 760,
                BowlingFrame.GAME_WIDTH/2, 800);
        graphToBack.setColor(temp);
        graphToBack.drawString("Press Space for Curve", 70, 750);
        graphToBack.drawLine(0, 760, 300, 760);
        graphToBack.drawLine(curve, 760, curve, 800);
        if (BowlingFrame.GAME_WIDTH/2 + 100 < curve)
            isIncreasing = false;
        if (BowlingFrame.GAME_WIDTH/2 - 100 > curve)
            isIncreasing = true;
        if (pressed[4])
            {
            pressed[4] = false;
            lastState = currentState;
            currentState = BowlingGameState.BOWL_MODE;
            }
        }

    /**
     * Calls this method in main game loop if state == bowl mode
     * @param graphToBack
     */
    protected void bowlState(Graphics graphToBack)
        {
        int pow = this.power, cur = (int)(curve-BowlingFrame.GAME_WIDTH/2);
        ball.setVel(cur/25, -pow/5 - 2);
        ball.update();
        if (((BowlingBall)(ball)).roll())
            {
            int score = 10 - bowlingPins.size();
            ball = new BowlingBall();
            lastState = currentState;
            currentState = BowlingGameState.POWER_MODE;
            rolls++;
            if (rolls > 1)
                score -= lastScore;
//                System.out.println(players[player]);
            players[player].addToSparesAndStrikes(score, turn);
            lastScore = score = 10 - bowlingPins.size();
            if (rolls > 1 || bowlingPins.size() == 0)
                {
                players[player].setScore(turn, score, 3 - rolls);
                if (player + 2 > players.length)
                    {
                    player = 0;
                    turn++;
                    }
                else
                    player++;
                rolls = 0;
                addPins();
                }
            }
        }   

    }
